import React from 'react';
import { VariableSizeGrid as Grid, VariableSizeList as List, areEqual } from 'react-window';
import classNames from 'classnames';
import { sum } from 'lodash';
import { Empty } from 'antd';
import { useCreation } from 'ahooks';
import type { ColumnType } from 'antd/es/table';
import ResizeObserver from 'rc-resize-observer';
import Styles from './useVirtualTable.module.less';
import scrollbarWidth from './scrollbarWidth';

interface IUseVirtualTable {
  columnList: ColumnType<any>[];
  render: (params: {
    rowIndex: number;
    columnIndex: number;
    rowInfo: object;
    style: React.CSSProperties; // 必须把 style渲染到 item上
  }) => React.ReactElement;
  getColumnWidth: (columnIndex?: number) => number;
  itemHeight: number;
  selected?: React.Key[]; // checkbox勾选key集合
  // 表格整体高度
  tableHeight: number;
  paginationHeight?: number; // 分页区域高度
  fixedClassName?: string;
}

export default ({
  columnList,
  getColumnWidth,
  render,
  itemHeight,
  selected = [],
  tableHeight,
  paginationHeight = 36,
  fixedClassName,
}: IUseVirtualTable) => {
  const [positionInfo, setPositionInfo] = React.useState<
    | {
        width: number; // body宽度
        height: number; // body高度
        scrollWidth: number; // 滚动条高度
      }
    | undefined
  >();

  const gridRef = React.useRef<any>();
  const leftRef = React.useRef<any>();
  const rightRef = React.useRef<any>();
  // 这里不兼容中间的情况
  const { leftTotal, rightTotal, leftWidth, rightWidth } = React.useMemo(() => {
    let leftTmp = 0;
    let rightTmp = 0;
    let leftWithTmp = 0;
    let rightWithTmp = 0;
    columnList.forEach((columnInfo, index) => {
      if (columnInfo.fixed === 'left') {
        leftTmp += 1;
        leftWithTmp += getColumnWidth(index);
      }
      if (columnInfo.fixed === 'right') {
        rightTmp += 1;
        rightWithTmp += getColumnWidth(index);
      }
    });
    return {
      leftTotal: leftTmp,
      rightTotal: rightTmp,
      leftWidth: leftWithTmp,
      rightWidth: rightWithTmp,
    }; // [leftTmp, rightTmp];
  }, columnList);

  // header滚动 同步 表格滚动
  const [connectObject] = React.useState<any>(() => {
    const obj = {};
    Object.defineProperty(obj, 'scrollLeft', {
      get: () => {
        if (gridRef.current) {
          return gridRef.current?.state?.scrollLeft;
        }
        return null;
      },
      set: (scrollLeft: number) => {
        if (gridRef.current) {
          gridRef.current.scrollTo({ scrollLeft });
        }
      },
    });

    return obj;
  });

  // 计算宽度  等
  const wrapperRef = React.useRef<any>();
  React.useEffect(() => {
    if (!wrapperRef.current) {
      return;
    }
    const { offsetWidth: width, offsetHeight: height } = wrapperRef.current;
    if (!positionInfo) {
      setPositionInfo({
        width,
        height,
        scrollWidth: scrollbarWidth(),
      });
    }
  }, [wrapperRef]);

  const getRowFn = useCreation(() => {
    const LeftRow = React.memo(
      ({
        index: rowIndex,
        style,
        data,
      }: {
        index: number;
        style: React.CSSProperties;
        data: object;
      }) => {
        return (
          <div style={{ ...style, height: itemHeight }} className={Styles.row}>
            {[...new Array(leftTotal)].map((val, index: number) => {
              return render({
                rowIndex,
                columnIndex: index,
                rowInfo: data[rowIndex],
                style: {},
              });
            })}
          </div>
        );
      },
      areEqual,
    );

    const RightRow = React.memo(
      ({
        index: rowIndex,
        style,
        data,
      }: {
        index: number;
        style: React.CSSProperties;
        data: object;
      }) => {
        return (
          <div style={{ ...style, height: itemHeight }}>
            {[...new Array(rightTotal)].map((val, index: number) => {
              return render({
                rowIndex,
                columnIndex: columnList.length - rightTotal + index,
                rowInfo: data[rowIndex],
                style: {},
              });
            })}
          </div>
        );
      },
      areEqual,
    );

    const Row = React.memo(
      ({
        columnIndex,
        rowIndex,
        style,
        data,
      }: {
        columnIndex: number;
        rowIndex: number;
        style: React.CSSProperties;
        data: object;
      }) => {
        if (rightTotal !== 0 && columnIndex + leftTotal >= columnList.length - rightTotal) {
          return (
            <div
              style={{
                ...style,
                left: leftWidth + Number(style.left),
              }}
            />
          );
        }

        return render({
          rowIndex,
          columnIndex: columnIndex + leftTotal,
          rowInfo: data[rowIndex],
          style: {
            ...style,
            left: leftWidth + Number(style.left),
          },
        });
      },
      areEqual,
    );
    return {
      LeftRow,
      RightRow,
      Row,
    };
  }, [leftTotal, rightTotal, selected, positionInfo?.width]);

  const { LeftRow, RightRow, Row } = getRowFn;

  //  { scrollbarSize, ref, onScroll }
  const renderVirtualList = (rawData: Record<string, unknown>[], { ref, onScroll }: any) => {
    // eslint-disable-next-line
    ref.current = connectObject;
    // 真实的宽度
    const realWidth = sum(
      columnList.map((n) =>
        typeof n.width === 'number' ? n.width : parseInt(n.width as string, 10),
      ),
    );
    const hasXScroll = realWidth > (positionInfo?.width || 0); // 出现横向滚动条
    // 内容占用高度
    const totalHeight = itemHeight * rawData.length;
    const scrollHeight = tableHeight - (itemHeight + paginationHeight);
    let wrapperStyle = {};
    const contentHeight = totalHeight >= scrollHeight ? scrollHeight : totalHeight;
    const bodyHeight = hasXScroll
      ? contentHeight + (positionInfo?.scrollWidth || 0)
      : contentHeight;
    const hasYScroll = totalHeight >= scrollHeight; // 是否出现纵向滚动条
    if (positionInfo) {
      wrapperStyle = {
        height: bodyHeight,
      };
    }

    return (
      <ResizeObserver
        onResize={({ width, height }: { height: number; width: number }) => {
          const difference = width - (positionInfo?.width || 0);
          if (difference > 10 || difference < -10 || difference === 0) {
            setPositionInfo({
              width,
              height,
              scrollWidth: scrollbarWidth(),
            });
          }
        }}
      >
        {!rawData.length ? (
          <Empty image={Empty.PRESENTED_IMAGE_SIMPLE} />
        ) : (
          <div ref={wrapperRef} className={Styles.inlineWrapper} style={wrapperStyle}>
            {/* 隐藏滚动条 */}
            {positionInfo && (
              <>
                {leftTotal > 0 && (
                  <List
                    ref={leftRef}
                    itemCount={rawData.length}
                    itemSize={(index: number) => {
                      return index ? itemHeight : itemHeight;
                    }}
                    className={classNames(Styles.leftWrapper, Styles.hideScroll, {
                      [Styles.leftShadow]:
                        leftTotal > 1 || columnList?.[0].dataIndex !== 'batchMode',
                    })}
                    width={leftWidth}
                    height={bodyHeight - (hasXScroll ? positionInfo.scrollWidth : 0)}
                    itemData={rawData}
                    onScroll={({ scrollOffset, scrollUpdateWasRequested }) => {
                      if (scrollUpdateWasRequested === false) {
                        gridRef?.current?.scrollTo({
                          scrollTop: scrollOffset,
                        });
                        rightRef?.current?.scrollTo(scrollOffset);
                      }
                    }}
                  >
                    {LeftRow}
                  </List>
                )}
                <Grid
                  itemData={rawData}
                  ref={gridRef}
                  columnCount={columnList.length - leftTotal}
                  columnWidth={(index) => {
                    const temp = getColumnWidth(leftTotal + index);
                    return index + leftTotal === columnList.length - 1
                      ? temp - positionInfo.scrollWidth
                      : temp;
                  }}
                  height={bodyHeight}
                  rowCount={rawData.length}
                  rowHeight={() => itemHeight}
                  width={positionInfo.width}
                  onScroll={({ scrollTop, scrollLeft, scrollUpdateWasRequested }) => {
                    if (scrollUpdateWasRequested === false) {
                      leftRef?.current?.scrollTo(scrollTop);
                      rightRef?.current?.scrollTo(scrollTop);
                    }
                    onScroll({ scrollLeft });
                  }}
                >
                  {Row}
                </Grid>
                {rightTotal > 0 && (
                  <List
                    itemData={rawData}
                    ref={rightRef}
                    itemCount={rawData.length}
                    itemSize={(index: number) => {
                      return index ? itemHeight : itemHeight;
                    }}
                    style={{
                      right: hasYScroll ? positionInfo.scrollWidth : 0,
                    }}
                    className={classNames(Styles.rightWrapper, Styles.hideScroll)}
                    width={rightWidth - (hasYScroll ? positionInfo.scrollWidth : 0)}
                    height={bodyHeight - (hasXScroll ? positionInfo.scrollWidth : 0)}
                    onScroll={({ scrollOffset, scrollUpdateWasRequested }) => {
                      if (scrollUpdateWasRequested === false) {
                        gridRef?.current?.scrollTo({
                          scrollTop: scrollOffset,
                        });
                        leftRef?.current?.scrollTo(scrollOffset);
                      }
                    }}
                  >
                    {RightRow}
                  </List>
                )}
              </>
            )}
          </div>
        )}
      </ResizeObserver>
    );
  };

  React.useLayoutEffect(() => {
    // 冻结表头
    const fixedDom: any = document.querySelectorAll(`.${fixedClassName}`);
    if (fixedDom[0]?.style.left === '0px' && fixedDom[1]?.style.left === '0px') {
      Array.prototype.slice.call(document.querySelectorAll(`.${fixedClassName}`)).forEach((dom) => {
        // eslint-disable-next-line
        dom.style.left = `${dom.offsetLeft}px`;
      });
    }
  }, [positionInfo?.width]);

  return {
    renderVirtualList,
  };
};
